package com.xiongjie.SingleLimit.algorithm;

import java.io.IOException;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;

/**
 * 使用令牌桶算法实现限流功能。每次从令牌桶中拿取令牌，能够拿到则请求继续，否则请求丢弃。
 * <p>
 * 使用BlockQueue
 */
public class TBLimiterWithBQAndQz implements Runnable {

    private ArrayBlockingQueue<Object> tokenQueue = new ArrayBlockingQueue<>(20);
    private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

    private ReentrantLock lock = new ReentrantLock(true);   //并发锁

    @Override
    public void run() {
        for (int i = 0; i < 2; i++) {
            tokenQueue.offer(new Object());
        }
        System.out.println("令牌数=" + tokenQueue.size());
    }

    /**
     * 1.发送请求，判断是否能够获取足够的令牌个数
     * 这里是令牌桶的核心业务代码
     *
     * @return
     */
    public boolean visit() {

        final ReentrantLock lock = this.lock;
        lock.lock();

        try {
            if(tokenQueue.size()<=0){
                return false;
            }else{
                Object token= tokenQueue.poll();

                //TODO 取出令牌，可以进行后续操作，可以在lock中，也可以在后续方法中

                return true;
            }
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        TBLimiterWithBQAndQz tbLimiterWithBQ = new TBLimiterWithBQAndQz();
        tbLimiterWithBQ.scheduledExecutorService.scheduleAtFixedRate(tbLimiterWithBQ, 0, 1, TimeUnit.SECONDS);

        //模拟0.1秒中发送一次请求,每次请求获取多个令牌
        IntStream.range(0, 2).forEach(i -> {
            new Thread(() -> {
                while (true) {
                    boolean res = tbLimiterWithBQ.visit();
                    System.out.print(res + "\t");
                    Random random=new Random();
                    int r=random.nextInt(2000);
                    try {
                        Thread.sleep(r);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        });
    }
}
